// AutoPtr.h
// (c) 2008, Charles Lechasseur
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef RRO_UTILS_COM_AUTOPTR_H
#define RRO_UTILS_COM_AUTOPTR_H

#pragma once

#include <windows.h>
#include "Exceptions/Exception.h"

#pragma warning( push )
#pragma warning( disable : 4290 )   // C++ exception specification ignored

namespace RRO {
namespace COM
{
    //
    // Exception thrown by the -> operator of AutoPtr when the object reference is null.
    //
    class NullObjectException : public Exception
    {
    public:
                                NullObjectException(void);
                                NullObjectException(const NullObjectException& p_Exception);
        NullObjectException&    operator=(const NullObjectException& p_Exception);
        virtual                 ~NullObjectException(void);

        virtual const char*     what(void) const;
    };

    //
    // Stack-based class for storing a COM object pointer.
    // The constructor calls AddRef and the destructor calls Release automatically on the object.
    //
    template<class IntfT>
    class AutoPtr
    {
    public:
                            //
                            // Default constructor. Sets the contained object to null.
                            //
                            AutoPtr(void)
                                : m_pObject(0)
                            {
                            }

                            //
                            // Constructor that acquires the given object.
                            //
                            // @param p_pObject Object to acquire. May be null.
                            // @param p_AddRef Whether to call AddRef on the object. Defaults to true.
                            //
        explicit            AutoPtr(IntfT* p_pObject, bool p_AddRef = true)
                                : m_pObject(p_pObject)
                            {
                                if (p_AddRef && (m_pObject != 0)) {
                                    m_pObject->AddRef();
                                }
                            }

                            //
                            // Copy constructor. Acquires a reference to the same object as
                            // another smart pointer.
                            //
                            // @param p_AutoPtr Smart pointer to copy.
                            //
                            AutoPtr(const AutoPtr<IntfT>& p_AutoPtr)
                                : m_pObject(p_AutoPtr.m_pObject)
                            {
                                // Call AddRef on the object, since this is a new reference.
                                if (m_pObject != 0) {
                                    m_pObject->AddRef();
                                }
                            }

                            //
                            // Assignment operator.
                            // Acquires a reference to the same object as another smart pointer.
                            //
        AutoPtr<IntfT>&     operator=(const AutoPtr<IntfT>& p_AutoPtr)
                            {
                                // Set() is self-assignment-safe, so we can call it directly.
                                Set(p_AutoPtr.Get());
                                return *this;
                            }

                            //
                            // Destructor. Releases the object reference if we have one.
                            //
                            ~AutoPtr(void)
                            {
                                Release();
                            }

                            //
                            // Returns the object reference stored in this smart pointer. May be null.
                            //
        IntfT*              Get() const
                            {
                                return m_pObject;
                            }

                            //
                            // Changes the value of the object stored in this smart pointer to 
                            // to point to another object.
                            //
                            // @param p_pObject New object to acquire. May be null.
                            // @param p_AddRef Whether to call AddRef on the new object. Defaults to true.
                            //
        void                Set(IntfT* p_pObject, bool p_AddRef = true)
                            {
                                // Use the "retain, then release" mechanism to avoid having to
                                // check for self-assignment.
                                IntfT* pOldObject = m_pObject;
                                m_pObject = p_pObject;
                                if (p_AddRef && (m_pObject != 0)) {
                                    m_pObject->AddRef();
                                }
                                if (pOldObject != 0) {
                                    pOldObject->Release();
                                }
                            }

                            //
                            // Releases and clears the object reference stored in this smart pointer.
                            //
        void                Release(void)
                            {
                                if (m_pObject != 0) {
                                    m_pObject->Release();
                                }
                                m_pObject = 0;
                            }

                            //
                            // Returns the object reference stored in this smart pointer. May be null.
                            //
                            operator IntfT*(void) const
                            {
                                return m_pObject;
                            }

                            //
                            // Returns the object reference stored in this smart pointer.
                            // NOTE: if the object reference is null when this is called, a NullObjectException
                            // will be thrown. Make sure object is not null by calling IsNull() before.
                            //
        IntfT*              operator->() const throw(NullObjectException)
                            {
                                if (m_pObject == 0) {
                                    throw NullObjectException();
                                }
                                return m_pObject;
                            }

                            //
                            // Checks to see if this smart pointer has a null object reference.
                            //
        bool                IsNull(void) const
                            {
                                return m_pObject == 0;
                            }

    private:
        // Reference to the object. May be null.
        IntfT*              m_pObject;
    };

    //
    // Stack-based class for querying an object for an interface and storing the reference.
    // Inherits from AutoPtr, so the result of the QI call is cached in the smart pointer.
    //
    template<class IntfT, REFIID QIIID> 
    class AutoQIPtr : public AutoPtr<IntfT>
    {
    public:
                                    //
                                    // Default constructor. Sets our object reference to null.
                                    //
                                    AutoQIPtr(void)
                                        : AutoPtr(),
                                          m_QIResult(S_OK)
                                    {
                                    }

                                    //
                                    // Constructor. Takes an object reference as parameter and queries
                                    // it for our interface type. Stores the result in this smart pointer.
                                    //
                                    // @param p_pObject Object to query. May be null.
                                    //
    explicit                        AutoQIPtr(IUnknown* p_pObject)
                                        : AutoPtr(),
                                          m_QIResult(S_OK)
                                    {
                                        // Let's QI the object for our interface.
                                        if (p_pObject != 0) {
                                            IntfT* pQIObject = 0;
                                            m_QIResult = p_pObject->QueryInterface(QIIID, &pQIObject);
                                            Set(pQIObject);
                                        }
                                    }

                                    //
                                    // Copy constructor. Stores the same object reference as
                                    // another smart pointer.
                                    //
                                    // @param p_AutoQIPtr Smart pointer to copy.
                                    //
                                    AutoQIPtr(const AutoQIPtr<IntfT, QIIID>& p_AutoQIPtr)
                                        : AutoPtr(p_AutoQIPtr),
                                          m_QIResult(p_AutoQIPtr.m_QIResult)
                                    {
                                        // Nothing else to do: since the smart pointer is of the proper type,
                                        // the AutoPtr constructor has copied the value.
                                    }

                                    //
                                    // Assignment operator. Stores a reference to the same object as
                                    // the given smart pointer.
                                    //
                                    // @param p_AutoQIPtr Smart pointer to copy.
                                    //
        AutoQIPtr<IntfT, QIIID>&    operator=(const AutoQIPtr<IntfT, QIIID>& p_AutoQIPtr)
                                    {
                                        // AutoPtr's assignment operator is self-assignment-safe.
                                        AutoPtr::operator=(p_AutoQIPtr);
                                        m_QIResult = p_AutoQIPtr.m_QIResult;
                                    }

                                    //
                                    // Destructor.
                                    //
                                    ~AutoQIPtr(void)
                                    {
                                        // Nothing to do, base class releases the object reference.
                                    }

                                    //
                                    // Returns the result of the QueryInterface call made in
                                    // the constructor to get the object reference.
                                    //
        HRESULT                     QueryInterfaceResult(void) const
                                    {
                                        return m_QIResult;
                                    }

    private:
        // Result of the QueryInterface call made in the constructor.
        HRESULT                     m_QIResult;
    };

}; // namespace COM
}; // namespace RRO

#pragma warning( pop )

#endif RRO_UTILS_COM_AUTOPTR_H

